using System.Linq;
using FluentNHibernate.Mapping;
using NHibernate;
using NHibernate.Criterion;
using NHibernate.Linq;
using NHibernateInsight.Tests.Domain;
using NHibernateInsight.Tests.Infrastructure;
using NHibernateInsight.Tests.Insights.Linq.Extensions;
using NUnit.Framework;
using Order=NHibernateInsight.Tests.Domain.Order;

namespace NHibernateInsight.Tests.Insights.Linq
{
    [TestFixture]
    public class Adding_criteria_to_Linq_query : TestContext
    {
        private class Mappings_of
        {
            public class Product : ClassMap<Domain.Product>
            {
                public Product()
                {
                    Id(x => x.Id)
                        .GeneratedBy.Native();
                    HasManyToMany(x => x.Prospects)
                        .Cascade.SaveUpdate()
                        .Inverse()
                        .AsBag();
                }
            }
            public class Prospect : ClassMap<Domain.Prospect>
            {
                public Prospect()
                {
                    Id(x => x.Id)
                        .GeneratedBy.Native();
                    Map(x => x.Initials);
                    Map(x => x.Sex);
                    HasManyToMany(x => x.Products)
                        .Cascade.SaveUpdate()
                        .AsBag();
                    HasManyToMany(x => x.ProductsSet)
                        .Cascade.SaveUpdate()
                        .Table("ProductsToProspects")
                        .AsSet();
                    HasMany(x => x.Orders)
                        .Cascade.SaveUpdate()
                        .Inverse()
                        .AsBag();
                    HasMany(x => x.OrdersSet)
                        .Cascade.SaveUpdate()
                        .Inverse()
                        .AsSet();
                    HasMany(x => x.Addresses)
                        .Cascade.SaveUpdate()
                        .Inverse()
                        .AsBag();
                    HasMany(x => x.AddressesSet)
                        .Cascade.SaveUpdate()
                        .Inverse()
                        .AsSet();
                }
            }
            public class Order : ClassMap<Domain.Order>
            {
                public Order()
                {
                    Id(x => x.Id)
                        .GeneratedBy.Native();
                    References(x => x.Prospect)
                        .Cascade.SaveUpdate();
                }
            }
            public class Address : ClassMap<Domain.Address>
            {
                public Address()
                {
                    Id(x => x.Id)
                        .GeneratedBy.Native();
                    References(x => x.Prospect)
                        .Cascade.SaveUpdate();
                }
            }
        }

        [SetUp]
        public void Given_that()
        {
            PrepareForSql2005ExpressDb();


            HideCreateAndDropDbSqlFromConsole();
            HideCreateAndDropSchemaSqlFromConsole();

            // Prepare data!
            Init(m => m.FluentMappings
                          .Add<Mappings_of.Product>()
                          .Add<Mappings_of.Prospect>()
                          .Add<Mappings_of.Order>()
                          .Add<Mappings_of.Address>());

            var product1 = new Product();
            var product2 = new Product();
            var product3 = new Product();
            var order1 = new Order();
            var order2 = new Order();
            var order3 = new Order();
            var order4 = new Order();
            var order5 = new Order();
            var address1 = new Address();
            var address2 = new Address();
            var prospect = new Prospect() { Initials = "Initials", Sex = "M" };

            prospect.RelateTo(product1);
            prospect.RelateTo(product2);
            prospect.RelateTo(product3);
            prospect.RelateTo(order1);
            prospect.RelateTo(order2);
            prospect.RelateTo(order3);
            prospect.RelateTo(order4);
            prospect.RelateTo(order5);
            prospect.RelateTo(address1);
            prospect.RelateTo(address2);

            using (var tran = CurrentSession.BeginTransaction())
            {
                CurrentSession.SaveOrUpdate(prospect);

                tran.Commit();
            }

            CurrentSession.Evict(product1);
            CurrentSession.Evict(product2);
            CurrentSession.Evict(product3);
            CurrentSession.Evict(order1);
            CurrentSession.Evict(order2);
            CurrentSession.Evict(order3);
            CurrentSession.Evict(order4);
            CurrentSession.Evict(order5);
            CurrentSession.Evict(address1);
            CurrentSession.Evict(address2);
            CurrentSession.Evict(prospect);
        }

        [TearDown]
        public void Cleanup_database()
        {
            Cleanup();
        }


        /// <summary>
        /// Then the query when executes will have the added Criterions in Where clause
        /// </summary>
        [Test]
        public void When_adding_Criterions_to_an_existing_Linq_query()
        {
            var query = CurrentSession.Linq<Prospect>();
                query.Where(p => p.Initials == "Initials");
                query.ExpandQuery(c => c.Add(Restrictions.Eq("Sex", "M")));

            
            var list = query.ToList();

            Output("Prospects with initials 'Initials' and sex 'M' should be one:");
            foreach (var prospect in list)
            {
                Output(string.Format("- Prospect {0}, {1}", prospect.Initials, prospect.Sex));
            }
        }


    }
}